package com.blandware.atleap.webapp.listener;

import com.blandware.atleap.common.Constants;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.http.HttpSession;
import javax.servlet.http.HttpSessionAttributeListener;
import javax.servlet.http.HttpSessionBindingEvent;
import java.util.HashMap;


/**
 * UserCounterListener class used to count the current number
 * of active users for the applications.  Does this by counting
 * how many user objects are stuffed into the session.  It also grabs
 * these users and exposes them in the servlet context.
 *
 * @author Matt Raible <a href="mailto:matt@raibledesigns.com">&lt;matt@raibledesigns.com&gt;</a>
 * @author Andrey Grebnev <a href="mailto:andrey.grebnev@blandware.com">&lt;andrey.grebnev@blandware.com&gt;</a>
 * @web.listener
 */
public class UserCounterListener implements ServletContextListener, HttpSessionAttributeListener {

	/**
	 * Key to store number of logined users in servletContext.
	 * It contains Integer
	 */
	public static final String COUNT_KEY = "userCounter";

	/**
	 * Key to store logined users in servletContext.
	 * It contains HashMap with key String with sessionId and value User
	 */
	public static final String USERS_KEY = "userNames";

	protected transient final Log log = LogFactory.getLog(UserCounterListener.class);
	protected transient ServletContext servletContext;

	public synchronized void contextInitialized(ServletContextEvent sce) {
		servletContext = sce.getServletContext();
		servletContext.setAttribute(COUNT_KEY, new Integer(0));
		servletContext.setAttribute(USERS_KEY, new HashMap());
	}

	public synchronized void contextDestroyed(ServletContextEvent event) {
		servletContext = null;
	}

    /**
     * Adds a user: if he is new, he's added to list of logged in users and
     * the counter is incremented. Otherwise nothing happens.
     *
     * @param session   The HTTP session to identify user by
     * @param user      User to be added
     */
	synchronized void addUser(HttpSession session, Object user) {
		HashMap users = (HashMap) servletContext.getAttribute(USERS_KEY);

        Integer sessionHashCode = new Integer(session.hashCode());

        if ( !users.containsKey(sessionHashCode) ) {
			users.put(sessionHashCode, user);

			servletContext.setAttribute(COUNT_KEY, new Integer(users.size()));
			servletContext.setAttribute(USERS_KEY, users);
		}
	}

    /**
     * Removes a user: if he is in list of logged in users, he's removed from
     * it and the counter is decremented. Otherwise nothing happens.
     *
     * @param session   The HTTP session to identify user by
     * @param user      User to be removed
     */
	synchronized void removeUser(HttpSession session, Object user) {
		HashMap users = (HashMap) servletContext.getAttribute(USERS_KEY);

        Integer sessionHashCode = new Integer(session.hashCode());

        if ( users.containsKey(sessionHashCode) ) {
			users.remove(sessionHashCode);

			servletContext.setAttribute(COUNT_KEY, new Integer(users.size()));
			servletContext.setAttribute(USERS_KEY, users);
		}
	}

	/**
	 * This method is designed to catch when users login and record their names
	 *
	 * @see javax.servlet.http.HttpSessionAttributeListener#attributeAdded(javax.servlet.http.HttpSessionBindingEvent)
	 */
	public void attributeAdded(HttpSessionBindingEvent event) {
		if ( event.getName().equals(Constants.USER_KEY) ) {
			addUser(event.getSession(), event.getValue());
		}
	}

	/**
	 * When users logout, remove their names from the hashMap
	 *
	 * @see javax.servlet.http.HttpSessionAttributeListener#attributeRemoved(javax.servlet.http.HttpSessionBindingEvent)
	 */
	public void attributeRemoved(HttpSessionBindingEvent event) {
		if ( event.getName().equals(Constants.USER_KEY) ) {
			removeUser(event.getSession(), event.getValue());
		}
	}

	/**
	 * @see javax.servlet.http.HttpSessionAttributeListener#attributeReplaced(javax.servlet.http.HttpSessionBindingEvent)
	 */
	public void attributeReplaced(HttpSessionBindingEvent event) {
		if ( event.getName().equals(Constants.USER_KEY) ) {
			addUser(event.getSession(), event.getValue());
		}
	}

}
